using System;
using System.Reflection;
using System.Xml;
using UnityEngine;

namespace EventBehaviorTree
{
    public static class DelegateBuilder
    {
        //TODO: functionHolder need to be found by the Type
        public static Action[] GetAction(XmlNodeList nodeList, BindingFlags bindingFlgs, object funcHolder)
        {
            if (nodeList == null) return new Action[0];
            int actionListLength = 0;
            Action[][] tmpActionList = new Action[nodeList.Count][];
            for (int i = 0; i < nodeList.Count; i++)
            {
                XmlNode delegateNode = nodeList[i];
                #region Generate parameters
                XmlNodeList paramList = delegateNode.SelectNodes("Param");
                Type[] types = new Type[paramList.Count];
                object[] parameters = new object[paramList.Count];
                //string actionType = $"System.Action`{paramList.Count}[";
                for (int j = 0; j < paramList.Count; j++)
                {
                    XmlNode paramNode = paramList[j];
                    types[j] = Type.GetType(paramNode.Attributes["Type"].Value);
                    parameters[j] = Convert.ChangeType(paramNode.InnerText, types[j]);
                }
                #endregion
                #region Generate actions
                string[] funcNames = XmlHelper.GetXmlStringList(delegateNode.InnerXml.Split("<")[0]);
                Action[] actions = new Action[funcNames.Length];
                for (int j = 0; j < funcNames.Length; j++)
                {
                    MethodInfo methodInfo = funcHolder.GetType().GetMethod(funcNames[j], bindingFlgs);
                    if (methodInfo.IsGenericMethod)
                    {
                        Type[] genericTypes = new Type[methodInfo.GetGenericArguments().Length];
                        string[] typeString = XmlHelper.GetXmlStringList(delegateNode.Attributes["Generic"].Value);
                        for (int l = 0; l < typeString.Length; l++)
                        {
                            genericTypes[l] = Type.GetType(typeString[l]);
                        }
                        methodInfo = methodInfo.MakeGenericMethod(genericTypes);
                    }
                    actions[j] = delegate { methodInfo.Invoke(funcHolder, parameters); };
                }
                actionListLength += actions.Length;
                tmpActionList[i] = actions;
                #endregion
            }
            #region Integrate actions into lists
            Action[] actionList = new Action[actionListLength];
            int k = 0;
            for (int i = 0; i < nodeList.Count; i++)
            {
                Action[] action = tmpActionList[i];
                for (int j = 0; j < action.Length; j++, k++)
                {
                    actionList[k] = action[j];
                }
            }
            #endregion
            return actionList;
        }

        public static Func<bool> GetCondition(XmlNode delegateNode, BindingFlags bindingFlgs, object funcHolder)
        {
            if (delegateNode == null) return null;
            XmlNodeList paramList = delegateNode.SelectNodes("Param");
            Type[] types = new Type[paramList.Count];
            object[] parameters = new object[paramList.Count];
            for (int j = 0; j < paramList.Count; j++)
            {
                XmlNode paramNode = paramList[j];
                types[j] = Type.GetType(paramNode.Attributes["Type"].Value);
                parameters[j] = Convert.ChangeType(paramNode.InnerText, types[j]);
            }
            string funcName = XmlHelper.GetXmlString(delegateNode.InnerXml.Split("<")[0]);
            MethodInfo methodInfo = funcHolder.GetType().GetMethod(funcName, bindingFlgs);
            return delegate { return (bool)methodInfo.Invoke(funcHolder, parameters); };
        }
    }
}

